
class isu2lsu_driver extends uvm_driver #(tr_isu2lsu);
  virtual tlm_sys_if sysif;
  virtual xpip_intf inf;
  vvpn_t vpn;
  vpage_typ_e ptyp;
      
  `uvm_component_utils_begin(isu2lsu_driver)
    `uvm_field_int(vpn, UVM_ALL_ON)
    `uvm_field_enum(vpage_typ_e, ptyp, UVM_ALL_ON)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  	    
    if(!uvm_config_db#(virtual xpip_intf)::get(this, "", "inf", inf))
      `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});

    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
    inf.isu2lsu <= isu2lsu_def;
    foreach(inf.cfg.smVpn[i, j])
      inf.cfg.smVpn[i][j] <= vpn;
    foreach(inf.cfg.smTyp[i, j])
      inf.cfg.smTyp[i][j] <= page_typ_e'(ptyp);
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
    time runDelay = 0ns;
    tr_isu2lsu req, rsp;
    if(!uvm_config_db#(time)::get(this, "", "runDelay", runDelay))
      `uvm_warning("NOVIF", {"delay time not set", get_full_name(), ".runDelay"});
    #runDelay;
    @(posedge sysif.clk);
    forever begin
///      @(posedge sysif.clk);
      while(inf.cb.lsu2isu.vblock)
        @(posedge sysif.clk);
      seq_item_port.get_next_item(req);
      ->req.start;
      `uvm_info("lsu drv", $psprintf("trans from isu:\n%s", req.sprint()), UVM_FULL)
///    	inf.isu2lsu <= tr2s_isu2lsu(req);
      inf.isu2lsu.wrf.adr <= req.adr;
      inf.isu2lsu.wrf.vec <= req.vec;
      inf.isu2lsu.wrf.dw <= req.dw;
      inf.isu2lsu.wrf.wr <= req.wr;
      inf.isu2lsu.ltid <= req.ltid;
      inf.isu2lsu.lid <= req.lid;
      inf.isu2lsu.gid <= req.gid;
      inf.isu2lsu.ls <= req.ls;
      inf.isu2lsu.ch <= req.ch;
      inf.isu2lsu.lg <= req.lg;
      inf.isu2lsu.oid <= req.oid;
      inf.isu2lsu.asyn <= req.asyn;
      inf.isu2lsu.fun <= req.ls ? req.st : req.ld;
      inf.isu2lsu.req <= '1;
      repeat(MCV-1)
        @(posedge sysif.clk);
      inf.isu2lsu.last <= '1;
      @(posedge sysif.clk);
      inf.isu2lsu.last <= '0;
      inf.isu2lsu.req <= '0;
      seq_item_port.item_done();
    end
  endtask : run_phase
endclass : isu2lsu_driver

class rfu2lsu_driver extends uvm_driver #(tr_rfu2lsu);
  virtual tlm_sys_if sysif;
  virtual xpip_intf inf;
    
  `uvm_component_utils_begin(rfu2lsu_driver)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    if(!uvm_config_db#(virtual xpip_intf)::get(this, "", "inf", inf))
      `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});

    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
    inf.rfu2lsu <= rfu2lsu_def;
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
    time runDelay = 0ns;
    tr_rfu2lsu req, rsp;
    if(!uvm_config_db#(time)::get(this, "", "runDelay", runDelay))
      `uvm_warning("NOVIF", {"delay time not set", get_full_name(), ".runDelay"});
    #runDelay;
    @(posedge sysif.clk);
    forever begin
      seq_item_port.get_next_item(req);
      `uvm_info("lsu drv", $psprintf("trans from rfu:\n%s", req.sprint()), UVM_FULL)

      inf.rfu2lsu.imm <= req.imm;
      for(int i = 0; i < MCV; i++) begin
        for(int j = 0; j < NUM_SP; j++) begin
          inf.rfu2lsu.d[j] <= req.d[i*NUM_SP+j];
          inf.rfu2lsu.b[j] <= req.b[i*NUM_SP+j];
          inf.rfu2lsu.v[j] <= req.v[i*NUM_SP+j];
        end
        @(posedge sysif.clk);
      end
      seq_item_port.item_done();
    end
  endtask : run_phase
endclass : rfu2lsu_driver

class dcu2lsu0_driver extends uvm_driver #(tr_dcu2lsu0);
  virtual tlm_sys_if sysif;
  virtual xpip_intf inf;
    
  `uvm_component_utils_begin(dcu2lsu0_driver)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    if(!uvm_config_db#(virtual xpip_intf)::get(this, "", "inf", inf))
      `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});

    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
    inf.dcu2lsu <= dcu2lsu_def;
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
    time runDelay = 0ns;
    tr_dcu2lsu0 req, rsp;
    if(!uvm_config_db#(time)::get(this, "", "runDelay", runDelay))
      `uvm_warning("NOVIF", {"delay time not set", get_full_name(), ".runDelay"});
    #runDelay;
    @(posedge sysif.clk);
    forever begin
      inf.dcu2lsu.req.ld <= '0;
      seq_item_port.get_next_item(req);
      repeat(req.delay)
        @(posedge sysif.clk);
      `uvm_info("lsu drv", $psprintf("trans from dcu:\n%s", req.sprint()), UVM_FULL)
      inf.dcu2lsu.req.ld <= '1;
      inf.dcu2lsu.ld.id <= req.did;
      foreach(inf.dcu2lsu.ld.data[i, j, k])
        inf.dcu2lsu.ld.data[i][j][k] <= req.data[i*NUM_SP*WORD_BYTES + j*WORD_BYTES + k];
      foreach(inf.dcu2lsu.ld.exp[i, j])
        inf.dcu2lsu.ld.exp[i][j] <= req.ldexp[i*NUM_SP+j] ? exr_ok : exr_err;
      @(posedge sysif.clk);
      while(!inf.cb.lsu2dcu.rsp.ld)
        @(posedge sysif.clk);
      seq_item_port.item_done();
    end
  endtask : run_phase
endclass : dcu2lsu0_driver

class dcu2lsu1_driver extends uvm_driver #(tr_dcu2lsu1);
  virtual tlm_sys_if sysif;
  virtual xpip_intf inf;
    
  `uvm_component_utils_begin(dcu2lsu1_driver)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    if(!uvm_config_db#(virtual xpip_intf)::get(this, "", "inf", inf))
      `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});

    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
    inf.dcu2lsu <= dcu2lsu_def;
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
    time runDelay = 0ns;
    tr_dcu2lsu1 req, rsp;
    if(!uvm_config_db#(time)::get(this, "", "runDelay", runDelay))
      `uvm_warning("NOVIF", {"delay time not set", get_full_name(), ".runDelay"});
    #runDelay;
    @(posedge sysif.clk);
    forever begin
      inf.dcu2lsu.req.s <= '0;
      seq_item_port.get_next_item(req);
      repeat(req.delay)
        @(posedge sysif.clk);
      `uvm_info("lsu drv", $psprintf("trans from dcu:\n%s", req.sprint()), UVM_FULL)
      foreach(inf.dcu2lsu.s.exp[i, j])
        inf.dcu2lsu.s.exp[i][j] <= req.sexp[i*NUM_SP+j] ? exr_ok : exr_err;
      inf.dcu2lsu.req.s <= '1;
      inf.dcu2lsu.s.id <= req.sid;
      @(posedge sysif.clk);
      while(!inf.cb.lsu2dcu.rsp.s)
        @(posedge sysif.clk);
      seq_item_port.item_done();
    end
  endtask : run_phase
endclass : dcu2lsu1_driver

class lsu_monitor extends uvm_monitor;
  virtual tlm_sys_if sysif;
  virtual xpip_intf inf;
	event cov_transaction;
  bit is_dcu_reactive, is_isu_reactive, dcuSdReq, dcuAReq, dcuALS;
  
	uvm_analysis_port #(tr_lsu2isu) isu_port;
	uvm_analysis_port #(tr_lsu2rfu) rfu_port;
  uvm_analysis_port #(tr_lsu2dcu0) dcu0_port;
  uvm_analysis_port #(tr_lsu2dcu1) dcu1_port;
  
  `uvm_component_utils_begin(lsu_monitor)
    `uvm_field_int(is_dcu_reactive, UVM_ALL_ON)
    `uvm_field_int(is_isu_reactive, UVM_ALL_ON)
  `uvm_component_utils_end
  
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    rfu_port = new("rfu_port", this);
    isu_port = new("isu_port", this);
    dcu0_port = new("dcu0_port", this);
    dcu1_port = new("dcu1_port", this);
    
    if(!uvm_config_db#(virtual xpip_intf)::get(this, "", "inf", inf))
      `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});

    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
    forever begin
      @(posedge sysif.clk) begin
        if(is_dcu_reactive) begin
          inf.dcu2lsu.rsp.a <= $random();
          inf.dcu2lsu.rsp.sd <= $random();
        end
        if(is_isu_reactive)
          inf.isu2lsu.rsp <= $random();
        get_trans;
        dcuSdReq = inf.cb.lsu2dcu.req.sd && inf.cb.dcu2lsu.rsp.sd;
        dcuAReq = inf.cb.lsu2dcu.req.a && inf.cb.dcu2lsu.rsp.a;
        dcuALS = inf.cb.lsu2dcu.ar.ls;
      end
    end
  endtask : run_phase
  
  task get_trans();
    if(dcuAReq) begin
      tr_lsu2dcu0 tr;
      tr = new();
      tr.ls = dcuALS;
      tr.sm = inf.cb.lsu2dcu.ar.sm;
      tr.arid = inf.cb.lsu2dcu.ar.rid;
      tr.chadr = inf.cb.lsu2dcu.ar.chadr;
      foreach(inf.lsu2dcu.ar.adr[i, j])
        tr.adr[i*NUM_SMEM_BK+j] = inf.cb.lsu2dcu.ar.adr[i][j];
      foreach(inf.lsu2dcu.ar.v[i, j, k])
        tr.v[i*NUM_SMEM_BK*WORD_BYTES + j*WORD_BYTES + k] = inf.cb.lsu2dcu.ar.v[i][j][k];
      `uvm_info("lsu mon", $psprintf("trans to dcu0:\n%s", tr.sprint()), UVM_FULL)
      dcu0_port.write(tr);
    end
    
    if(dcuSdReq) begin
      tr_lsu2dcu1 tr;
      tr = new();
      tr.did = inf.cb.lsu2dcu.sd.id;
      foreach(inf.lsu2dcu.sd.data[i, j])
        tr.data[i*NUM_VEC+j] = inf.cb.lsu2dcu.sd.data[i][j];
      `uvm_info("lsu mon", $psprintf("trans to dcu1:\n%s", tr.sprint()), UVM_FULL)
      dcu1_port.write(tr);
    end
    
    if(inf.cb.lsu2isu.req && inf.cb.isu2lsu.rsp) begin
      tr_lsu2isu tr;
      tr = new();
      tr.adr = inf.cb.lsu2isu.adr;
      ///vblock, req, 
      tr.asyn = inf.cb.lsu2isu.asyn;
      tr.ls = inf.cb.lsu2isu.ls;
      tr.lid = inf.cb.lsu2isu.lid;
      tr.ltid = inf.cb.lsu2isu.ltid;
      tr.oid = inf.cb.lsu2isu.oid;
      `uvm_info("lsu mon", $psprintf("trans to isu:\n%s", tr.sprint()), UVM_FULL)
      isu_port.write(tr);
    end
    
    if(inf.cb.lsu2isu.req && inf.cb.isu2lsu.rsp) begin
      tr_lsu2rfu tr;
      tr = new();
      tr.adr = inf.cb.lsu2rfu.adr;
      tr.crfw = inf.cb.lsu2rfu.crfw;
      tr.flag = inf.cb.lsu2rfu.flag;
      foreach(inf.lsu2rfu.v[i, j]) begin
        tr.v[i*NUM_SP+j] = inf.cb.lsu2rfu.v[i][j];
        tr.data[i*NUM_SP+j] = inf.cb.lsu2rfu.data[i][j];
      end
      `uvm_info("lsu mon", $psprintf("trans to rfu:\n%s", tr.sprint()), UVM_FULL)
      rfu_port.write(tr);
    end
  endtask
endclass : lsu_monitor

typedef uvm_sequencer#(tr_isu2lsu) isu2lsu_sequencer;
typedef uvm_sequencer#(tr_rfu2lsu) rfu2lsu_sequencer;

class lsu_sequencer extends uvm_sequencer#(uvm_sequence_item);
  virtual tlm_sys_if sysif;
  isu2lsu_sequencer isu_seqr;
  rfu2lsu_sequencer rfu_seqr;
  vvpn_t vpn;
  vpage_typ_e ptyp;
  
  `uvm_component_utils_begin(lsu_sequencer)
    `uvm_field_int(vpn, UVM_ALL_ON)
    `uvm_field_enum(vpage_typ_e, ptyp, UVM_ALL_ON)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
///  	assert(uvm_config_db#(vvpn_t)::get(this, "", "vpn", vpn));
///  	assert(uvm_config_db#(vpage_typ_e)::get(this, "", "ptyp", ptyp));
  	    
    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
  endfunction : build_phase
  
  task run_phase(uvm_phase phase);
///    time runDelay = 0ns;
///    tr_rfu2lsu req, rsp;
///    if(!uvm_config_db#(time)::get(this, "", "runDelay", runDelay))
///      `uvm_warning("NOVIF", {"delay time not set", get_full_name(), ".runDelay"});
///    #runDelay;    
///    forever begin
///      seq_item_port.get_next_item(req);
///      `uvm_info("isu drv", $psprintf("trans from ifu:\n%s", req.sprint()), UVM_FULL)

///      seq_item_port.item_done();
///    end
  endtask : run_phase
endclass

class dcu2lsu0_sequencer extends uvm_sequencer#(tr_dcu2lsu0);
  virtual tlm_sys_if sysif;
  tr_lsu2dcu0 q[$];
  
  uvm_analysis_imp #(tr_lsu2dcu0, dcu2lsu0_sequencer) tr_export;
  
  `uvm_component_utils_begin(dcu2lsu0_sequencer)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  	tr_export = new("tr_export", this);
  	
    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
  endfunction : build_phase
  
  virtual function void write(tr_lsu2dcu0 tr);
    q.push_back(tr);
  endfunction : write
  
  task run_phase(uvm_phase phase);

  endtask : run_phase
endclass

class dcu2lsu1_sequencer extends uvm_sequencer#(tr_dcu2lsu1);
  virtual tlm_sys_if sysif;
  tr_lsu2dcu1 q[$];
  
  uvm_analysis_imp #(tr_lsu2dcu1, dcu2lsu1_sequencer) tr_export;
  
  `uvm_component_utils_begin(dcu2lsu1_sequencer)
  `uvm_component_utils_end

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  	tr_export = new("tr_export", this);
  	
    if(!uvm_config_db#(virtual tlm_sys_if)::get(this, "", "sysif", sysif))
      `uvm_fatal("NOVIF", {"system virtual interface must be set for: ", get_full_name(), ".sysif"});
  endfunction : build_phase
  
  virtual function void write(tr_lsu2dcu1 tr);
    q.push_back(tr);
  endfunction : write
  
  task run_phase(uvm_phase phase);
  endtask : run_phase
endclass

class lsu_agent extends uvm_agent;
  uvm_active_passive_enum is_active = UVM_ACTIVE;
  `uvm_component_utils_begin(lsu_agent)
    `uvm_field_enum(uvm_active_passive_enum, is_active, UVM_ALL_ON)
  `uvm_component_utils_end
  
  isu2lsu_sequencer isu_sequencer;
  rfu2lsu_sequencer rfu_sequencer;
  dcu2lsu0_sequencer dcu0_sequencer;
  dcu2lsu1_sequencer dcu1_sequencer;
  lsu_sequencer top_seqr;
  
  isu2lsu_driver isu_driver;
  rfu2lsu_driver rfu_driver;
  dcu2lsu0_driver dcu_driver0;
  dcu2lsu1_driver dcu_driver1;
  
  lsu_monitor monitor;
  
  function new(string name, uvm_component parent=null);
    super.new(name, parent);
  endfunction /// new
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    monitor = lsu_monitor::type_id::create("monitor",this);
    if (is_active == UVM_ACTIVE) begin
      isu_sequencer = isu2lsu_sequencer::type_id::create("isu_sequencer",this);
      rfu_sequencer = rfu2lsu_sequencer::type_id::create("rfu_sequencer",this);
      dcu0_sequencer = dcu2lsu0_sequencer::type_id::create("dcu0_sequencer",this);
      dcu1_sequencer = dcu2lsu1_sequencer::type_id::create("dcu1_sequencer",this);
      top_seqr = lsu_sequencer::type_id::create("top_seqr",this);
      
      top_seqr.isu_seqr = isu_sequencer;
      top_seqr.rfu_seqr = rfu_sequencer;
      
      isu_driver = isu2lsu_driver::type_id::create("isu_driver",this);
      rfu_driver = rfu2lsu_driver::type_id::create("rfu_driver",this);
      dcu_driver0 = dcu2lsu0_driver::type_id::create("dcu_driver0",this);
      dcu_driver1 = dcu2lsu1_driver::type_id::create("dcu_driver1",this);
    end
  endfunction : build_phase
  
  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    if(is_active == UVM_ACTIVE) begin
      isu_driver.seq_item_port.connect(isu_sequencer.seq_item_export);
      rfu_driver.seq_item_port.connect(rfu_sequencer.seq_item_export);
      dcu_driver0.seq_item_port.connect(dcu0_sequencer.seq_item_export);
      dcu_driver1.seq_item_port.connect(dcu1_sequencer.seq_item_export);
      monitor.dcu0_port.connect(dcu0_sequencer.tr_export);
      monitor.dcu1_port.connect(dcu1_sequencer.tr_export);
    end
  endfunction : connect_phase
endclass : lsu_agent